# include "allocation.h"

static Store *calca = NULL;
static Graph *allocationsa = NULL;
static int minHorarios = 0;

static void printSpacesLen (char *frase, int max) {
	int n = max - strlen(frase);
	int i;
	char *esr = strdup(frase);
	if (n<0) strncpy(esr,frase,max);
	printf("%s",esr);
	free(esr);
	for (i=0;i<n;i++) printf(" ");
}

static void writeUCEResult (int uce) {
	FILE *fp;
	char *uceName = calca->mapUCEs[uce];
	char *name = getProjectName();
	char *path = (char *) malloc(sizeof(char)*(strlen(RESULT_PATH)+strlen(name)+strlen(uceName)+10));
	sprintf(path,"%s/%s/%s",RESULT_PATH,name,uceName);
	fp = fopen(path,"w");
	Student *st;
	ArrayList *lst = calca->uces[uce];
	int i,n;
	if (fp) {
		if (lst) n = arrayGetLength(lst);
		for (i=0;i<n;i++) {
			st = arrayGet(lst,i);
			fprintf(fp,"%s (%d)\n",st->name,st->number);
		}
		fclose(fp);
	}
}
static void writeResult () {
	FILE *fp;
        char *name = getProjectName();
        char *path = (char *) malloc(sizeof(char)*(strlen(RESULT_PATH)+strlen(name)+20));
	sprintf(path,"%s",RESULT_PATH);
	fp = fopen(path,"r");
	if (!fp) mkdir(path,S_IRWXU | S_IRWXG | S_IRWXO);
	else fclose(fp);
        sprintf(path,"%s/%s",RESULT_PATH,name);
	fp = fopen(path,"r");
	if (!fp) mkdir(path,S_IRWXU | S_IRWXG | S_IRWXO);
	else fclose(fp);
	sprintf(path,"%s/%s/horario",RESULT_PATH,name);
        fp = fopen(path,"w");
	int i;
	if (fp) {
		fprintf(fp,"Horários (Varia de 1 a %d)\n\n\n",minHorarios);
		for (i=0;i<allocationsa->nodes;i++) {
			fprintf(fp," %s (Horário %d)\n",calca->mapUCEs[i],allocationsa->color[i]);
			writeUCEResult(i);
		}
		fclose(fp);
	}
	
}

int getHorariaEAlocacoes (ChainingHashMap *map, ArrayList *students, int horarios, int maxUCEsPorHorario, int modoInicial, int proOrdem, int extraOrdem, int maxBest, int grava) {
	int stop = 0; int a;
	Store *calc;
	Graph *allocations;
	calc = newStore();
	arraySort(students,compareStudent,1);
	fillInfo(calc,map,students);
	// alocacao inicial
	if (proOrdem == extraOrdem)
		initialDistribuition(calc,1,1,modoInicial);
	else if (proOrdem < extraOrdem)
		initialDistribuition(calc,1,0,modoInicial);
	else initialDistribuition(calc,0,1,modoInicial);
	allocations = getGraph(calc);
	allocations->maxColors = maxUCEsPorHorario;
	minHorarios = colorGraph(allocations);
	if (!modoInicial) {
		compoeAll(calc);
		minHorarios = colorGraph(allocations);
	}
	int exec = 0;
	while ((horarios && (minHorarios > horarios)) && !stop) {
		stop = !removeWorstEdge(calc);
		if (!stop) {
			exec = 1;
			if (maxBest) {
				// maximiza a felicidade dos melhores
				tryToMakeSomeoneHappy(calc);
			} else {
				tryToAllocateNonAllocated(calc);
			}
			freeGraph(allocations);
			allocations = getGraph(calc);
			allocations->maxColors = maxUCEsPorHorario;
			minHorarios = colorGraph(allocations);
		}
	}
	if (stop) return 0;
	if (exec) {
		if (maxBest) tryToAllocateNonAllocated(calc);
		else tryToMakeSomeoneHappy(calc);
	}
	if (proOrdem < extraOrdem) {
		allocateRole(calc,EXTRA,2);
	} else if (proOrdem > extraOrdem) {
		allocateRole(calc,PROFESSIONAL,2);
	}
	if (grava) {
		calca = calc;
		allocationsa = allocations;
	}
	return minHorarios;
}
void avisa (char *s) {
	printf("WARNING: Configuração %s não definida\n",s);
	printf("Para definir use o comando > setConfig %s <valor>\n",s);
}
CommandResult * command_allocateStudents (int argc, char *argv[]) {
	char *cfg = NULL;
	CommandResult *suc = successCommandResult();
	if (!hasProject()) {
		printf("Não existe project actualmente\n");
		return suc;
	}
	int horarios=0,maxUces = 0,mode=0,extra=0;
	int calc = 0;
	cfg = getConfigurationValue(CONFIG_HORARIOS);
	if (cfg) horarios = atoi(cfg);
	else avisa(CONFIG_HORARIOS);
	if (!horarios) {
		printf("Número de horários inválido!\n");
		return suc;
	}
	cfg = getConfigurationValue(CONFIG_MAX_UCES_POR_HORARIO);
	if (cfg) maxUces = atoi(cfg);
	else {
		avisa(CONFIG_MAX_UCES_POR_HORARIO);
		printf("Assumindo que não há limite para o número máximo de UCEs por horário\n");
	}
	cfg = getConfigurationValue(CONFIG_INITIAL_MODE);
	if (cfg) mode = atoi(cfg);
	else {
		avisa(CONFIG_INITIAL_MODE);
		printf("Assumindo modo de alocação inicial = 0\n");
	}
	if (mode < 0 || mode > 2) mode = 0;
	switch (mode) {
		case 0:
			printf("Modo inicial: Alocar sem ligar aos limites das UCEs\n");
			break;
		case 1:
			printf("Modo inicial: Alocar ligando aos limites (não se tenta adicionar o aluno a segundas opções)\n");
			break;
		case 2:
			printf("Modo inicial: Alocar ligando aos limites (tenta-se adicionar o aluno a segundas opções)\n");
			break;
	}
	cfg = getConfigurationValue(CONFIG_EXTRA_ORDER);
	if (cfg) extra = atoi(cfg);
	else {
		avisa(CONFIG_EXTRA_ORDER);
		printf("Assumindo que os alunos em extra curricular não são alocados inicialmente\n");
	}
	int a,b;
	if (extra) { a = 1; b = 1; }
	else { a = 1; b = 2; }
	cfg = getConfigurationValue(CONFIG_BEST_METHOD);
	int best = 0;
	if (cfg) best = atoi(cfg);
	else {
		avisa(CONFIG_BEST_METHOD);
		printf("Assumindo que se vai dar preferência à maioria\n");
	}
	if (calca) freeStore(calca);
	if (allocationsa) freeGraph(allocationsa);
	ChainingHashMap *map = getUCEsMap();
	LinkedList *aux,*lst = keySetChaining(map);
	int c = 0; aux = lst;
	while (lst) {
		c++; lst = lst->next;
	}
	freeLinkedList(aux);
	float f = (float) c / (float) horarios;
	if (f > maxUces) {
		printf("Impossivel obter horário - Solução: \n\t- Aumentar o número de horários\n\t- Aumentar o limite de UCEs em simultâneo\n");
		return suc;
	}
	int res = getHorariaEAlocacoes (getUCEsMap(), getStudentsList(), horarios, maxUces, mode, a, b, best,1);
	if (res) {
		printf("Alocações realizadas com sucesso!\nNúmero de horários usados: %d\n",res);
		writeResult();
	} else printf("Não foi possivel realizar alocações! Verifique as suas configurações e dados\n");
	return suc;
}

CommandResult * command_minHorarioMaxHappyness (int argc, char *argv[]) {
	char *cfg = NULL;
	Store *st = newStore();
        CommandResult *suc = successCommandResult();
        if (!hasProject()) {
                printf("Não existe project actualmente\n");
                return suc;
        }
        int horarios=0,maxUces = 0,mode=0,extra=0;
        int calc = 0;
        cfg = getConfigurationValue(CONFIG_INITIAL_MODE);
        if (cfg) mode = atoi(cfg);
        else {
                avisa(CONFIG_INITIAL_MODE);
                printf("Assumindo modo de alocação inicial = 0\n");
        }
        if (mode < 0 || mode > 2) mode = 0;
        switch (mode) {
                case 0:
                        printf("Modo inicial: Alocar sem ligar aos limites das UCEs\n");
                        break;
                case 1:
                        printf("Modo inicial: Alocar ligando aos limites (não se tenta adicionar o aluno a segundas opções)\n");
                        break;
                case 2:
                        printf("Modo inicial: Alocar ligando aos limites (tenta-se adicionar o aluno a segundas opções)\n");
                        break;
        }
	cfg = getConfigurationValue(CONFIG_EXTRA_ORDER);
        if (cfg) extra = atoi(cfg);
        else {
                avisa(CONFIG_EXTRA_ORDER);
                printf("Assumindo que os alunos em extra curricular não são alocados inicialmente\n");
        }
        int a,b;
        if (extra) { a = 1; b = 1; }
        else { a = 1; b = 2; }
        cfg = getConfigurationValue(CONFIG_BEST_METHOD);
        int best = 0;
        if (cfg) best = atoi(cfg);
        else {
                avisa(CONFIG_BEST_METHOD);
                printf("Assumindo que se vai dar preferência à maioria\n");
        }
        int res = getHorariaEAlocacoes (getUCEsMap(), getStudentsList(), horarios, maxUces, mode, a, b, best,0);
        if (res) {
                printf("Número minimo de horários: %d\n",res);
        } else printf("Não foi possivel realizar alocações! Verifique as suas configurações e dados\n");
        return suc;
}

CommandResult * command_ocupacao (int argc, char *argv[]) {
	// para cada uce imprimir a taxa de ocupacao
	CommandResult *suc = successCommandResult();
	if (!calca) {
		printf("Falta executar o comando de alocações\n");
		return suc;
	}
	float sum = 0;
	float actual;
	int i;
	int m,v; UCE *uce;
	ArrayList *lst;
	printf("Ocupação | Vagas | Alunos | UCE\n");
	for (i=0;i<calca->nUCEs;i++) {
		lst = calca->uces[i];
		uce = calca->uceInfo[i];
		m = 0;
		if (lst) m = arrayGetLength(lst);
		v = uce->maxStudents;
		if (v == 0) actual = 0;
		else actual = (float) m / (float) v;
		printf("  %0.3f  |  %3d  |  %3d   | %s\n",actual,v,m,calca->mapUCEs[i]);
		sum += actual;
	}
	sum = sum / (float) i;
	printf("\n\tMédia: %f\n",sum);
	return suc;
}

CommandResult * command_indiceForca (int argc, char *argv[]) {
	int i,j;
        CommandResult *suc = successCommandResult();
	if (!calca) {
		printf("Falta executar o comando alocações\n");
		return suc;
	}
	int sum[calca->nUCEs];
	for (i=0;i<calca->nUCEs;i++) sum[i] = 0;
	ArrayList *lst = getStudentsList();
	int n = arrayGetLength(lst);
	Student *st;
	for (i=0;i<n;i++) {
		st = arrayGet(lst,i);
		for (j=0;j<calca->nUCEs;j++) {
			if (isFirstSelection(st,calca->mapUCEs[j])) {
				sum[j]++;
			}
		}
	}
	int v,m; UCE *uce;
	float soma = 0;
	float actual;
	printf("Ind. Força | Vagas | Alunos | UCE\n");
        for (i=0;i<calca->nUCEs;i++) {
                uce = calca->uceInfo[i];
                v = uce->maxStudents;
                if (v == 0) actual = 0;
                else actual = (float) sum[i] / (float) v;
                printf("  % 3.3f   |  %3d  |  %3d   | %s\n",actual,v,sum[i],calca->mapUCEs[i]);
                soma += actual;
        }
        soma = soma / (float) i;
        printf("\n\tMédia: %f\n",soma);
        return suc;

}
CommandResult * command_potencial (int argc, char *argv[]) {
	int i,j;
        CommandResult *suc = successCommandResult();
        if (!calca) {
                printf("Falta executar o comando alocações\n");
                return suc;
        }
        ArrayList *lst;
        int n,sum[calca->nUCEs];
        for (i=0;i<calca->nUCEs;i++) sum[i] = 0;
        Student *st;
        for (j=0;j<calca->nUCEs;j++) {
                lst = calca->uces[j];
                n = arrayGetLength(lst);
                for (i=0;i<n;i++) {
                        st = arrayGet(lst,i);
                        if (isFirstSelection(st,calca->mapUCEs[j])) sum[j]++;
                }
        }
        int v,m; UCE *uce;
        float soma = 0;
        float actual;
        printf("Potencial  | Vagas | Alunos | UCE\n");
        for (i=0;i<calca->nUCEs;i++) {
                uce = calca->uceInfo[i];
                v = uce->maxStudents;
		v = v - getUCEgap(calca,i);
                if (v == 0) actual = sum[i];
                else actual = (float) sum[i] / (float) v;
                printf("  % 3.3f   |  %3d  |  %3d   | %s\n",actual,v,sum[i],calca->mapUCEs[i]);
                soma += actual;
        }
        soma = soma / (float) i;
        printf("\n\tMédia: %f\n",soma);
        return suc;
}
CommandResult * command_fraqueza (int argc, char *argv[]) {
        int i,j;
        CommandResult *suc = successCommandResult();
        if (!calca) {
                printf("Falta executar o comando alocações\n");
                return suc;
        }
	ArrayList *lst;
        int n,sum[calca->nUCEs];
        for (i=0;i<calca->nUCEs;i++) sum[i] = 0;
        Student *st;
        for (j=0;j<calca->nUCEs;j++) {
		lst = calca->uces[j];
		n = arrayGetLength(lst);
		for (i=0;i<n;i++) {
			st = arrayGet(lst,i);
			if (!isFirstSelection(st,calca->mapUCEs[j])) sum[j]++;
                }
        }
        int v,m; UCE *uce;
        float soma = 0;
        float actual;
        printf("Fraqueza   | Vagas | Alunos | UCE\n");
        for (i=0;i<calca->nUCEs;i++) {
                uce = calca->uceInfo[i];
                v = uce->maxStudents;
		v = v - getUCEgap(calca,i);
                if (v == 0) actual = 0;
                else actual = (float) sum[i] / (float) v;
                printf("  % 3.3f   |  %3d  |  %3d   | %s\n",actual,v,sum[i],calca->mapUCEs[i]);
                soma += actual;
        }
        soma = soma / (float) i;
        printf("\n\tMédia: %f\n",soma);
        return suc;

}
CommandResult * command_proximidade (int argc, char *argv[]) {
	int i,j,v;
        CommandResult *suc = successCommandResult();
        if (!calca) {
                printf("Falta executar o comando alocações\n");
                return suc;
        }
	if (argc > 0 && argc < 2) {
		printf("Faltam argumentos: '<uceA>' '<uceB>'");
		return suc;
	}
	if (argc == 0) {
		// imprimir tudo!
		printf(" Alunos | UCE - UCE\n");
		for (i=1;i<calca->nUCEs;i++) {
			for (j=0;j<i;j++) {
				v = getEdgeValue(calca->commonStudents,i,j);
				if (v>0) {
					printf("  % 3d   | %s,%s\n",v,calca->mapUCEs[i],calca->mapUCEs[j]);
				}
			}
		}
	} else {
		i = getUCEIndice(calca,argv[0]);
		j = getUCEIndice(calca,argv[1]);
		v = getEdgeValue(calca->commonStudents,i,j);
		printf("  % 3d   | %s - %s\n",v,calca->mapUCEs[i],calca->mapUCEs[j]);

	}
        return suc;
}

